在上一篇的內容中,想要實現讓 Pipeline 的所有 Job 都在同一個 Runner 上執行,目前打算使用 CI_RUNNER_TAGS
這個 Pre-defined 變數來達成,但實驗一裡頭又發現,因為 CI_RUNNER_TAGS
在 Job 建立之後才存在,因此無法直接在 CI/CD YAML 的 tags
使用,那麼接下來該怎麼做呢?
由於 CI_RUNNER_TAGS
在 Job 建立之後才存在,因此必須在第一個 Job 建立之後,在這個 Job 中,決定後續的 Job 該透過什麼 Runner 執行,而其實 GitLab CI/CD 的結構上,是在建立 Pipeline 時,就已經決定該有哪些 Job,執行,剛好與前面的所提到的有所衝突。
唯一可以達成這個效果的,就會是動態的 Pipeline 建立,也就是 Parent Child Pipeline,在第一個 Job 取得目前 Runner 的 CI_RUNNER_TAGS
之後,設定 Child Pipeline 的 YAML 指定 tags
為 CI_RUNNER_TAGS
。
具體的實作如下,首先先建立一個預計用來動態建立的 GitLab CI/CD YAML dynamic-child.yml
,可以注意到,這邊的內容還沒有指定 tags
,因為這部分要在 .gitlab-ci.yml
中的 Script 動態指定:
# filename dynamic-child.yml
default:
image: ubuntu:24.04
child-pipeline-01:
script:
- echo 'This is child pipeline 01'
- echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
needs:
- pipeline: $PARENT_PIPELINE_ID
job: detect-runner
如上面所說,在 Job 建立之後,就有 CI_RUNNER_TAGS
可以使用,因此,可以透過 Script 在取得 dynamic-child.yml
作為 GitLab CI/CD YAML 的基底之後,進行一些「編輯」,這邊的做法是透過 echo 的方法寫入到 child-job.yml
中。
# filename .gitlab-ci.yml
default:
image: ubuntu:24.04
stages:
- build
- child-job
detect-runner:
stage: build
tags: [gitlab-org]
script:
- echo "detect runner"
- echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
- cp dynamic-child.yml child-job.yml
- echo " tags:" "$CI_RUNNER_TAGS" >> child-job.yml
artifacts:
paths:
- child-job.yml
trigger-child-job:
stage: child-job
trigger:
include:
- artifact: child-job.yml
job: detect-runner
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
要特別注意,因為 :
符號,在 GitLab CI/CD YAML 中是特殊符號,所以 script 中如果有出現這個符號必須要特別處理,因此 echo 的那段才會透過兩組字串來組成內容。
- cp dynamic-child.yml child-job.yml
- echo " tags:" "$CI_RUNNER_TAGS" >> child-job.yml
在建立好 child-job.yml
之後,透過 artifacts
傳遞給下一階段的 Job trigger-child-job
用來 trigger 建立 Child Pipeline。這邊用了前面的內容有提到的 needs:pipeline
的方法,方便之後取得 Job Artifact。
到這邊為止,基本的架構已經成形可用了,但是還有一些問題需要解決。例如 Parent Child Pipeline 如果要在 Parent Child 之間傳遞變數?傳遞 Artifact 該如何處理?
在 Parent-Child Pipeline 之間傳遞變數、檔案的方法,其實在實驗二裡頭,已經預埋了,也就是 needs:pipeline
的使用,細節可以參考 Day16 - 在 CI/CD YAML 語法 needs 中取得父層流水線的 Artifact - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天的內容,其修改微調之後的內容如下,這部分先說明 .gitlab-ci.yml
:
在 detect-runner
這個 Job 裡頭,一樣透過 echo
的方式,在 variables.txt 檔案中,建立變數 TEST_VAR
其數值為 TestValue
,並且把 variables.txt 加入到 artifacts 的清單中。
在 trigger-child-job
的工作中,也將一些預計帶入到 child-pipeline 的變數置於 variables
的段落中,如 VARIABLE01
。
# filename .gitlab-ci.yml
default:
image: ubuntu:24.04
stages:
- build
- child-job
detect-runner:
stage: build
tags: [gitlab-org]
script:
- echo "detect runner"
- echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
- cp dynamic-child.yml child-job.yml
- echo " tags:" "$CI_RUNNER_TAGS" >> child-job.yml
- echo " ========================== "
- cat child-job.yml
- echo "TEST_VAR=TestValue" >> variables.txt
artifacts:
paths:
- child-job.yml
- variables.txt
trigger-child-job:
stage: child-job
trigger:
include:
- artifact: child-job.yml
job: detect-runner
forward:
pipeline_variables: true
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
VARIABLE01: "is variable 01"
在 dynamic-child.yml
裡頭,假設需要建立多個 Job,因此都會需要設定相同的 tags
所以我們把所有 Job 共同的區段,包含之後要在 .gitlab-ci.yml
寫入 tags
的段落,放在檔案的最底端,建立為 .common_tags
供其他的 Job 透過 extends
的方式引用。
# filename dynamic-child.yml
default:
image: ubuntu:24.04
child-pipeline-01:
extends: .common_tags
script:
- echo 'This is child pipeline 01'
- echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
- echo "VARIABLE01 = $VARIABLE01"
- cat variables.txt
- source variables.txt
- echo "TEST_VAR = $TEST_VAR"
child-pipeline-02:
extends: .common_tags
script:
- echo 'This is child pipeline 02'
- echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
.common_tags:
needs:
- pipeline: $PARENT_PIPELINE_ID
job: detect-runner
在 child-pipeline-01
Job 的內容中,我們試著以 echo 的方法使用從 .gitlab-ci.yml
帶過來的變數,這邊有透過 source
方法帶入 variables.txt
。其輸出結果如下,如預期的輸出:
$ echo 'This is child pipeline 01'
This is child pipeline 01
$ echo "CI_RUNNER_TAGS = $CI_RUNNER_TAGS"
CI_RUNNER_TAGS = ["gitlab-org"]
$ echo "VARIABLE01 = $VARIABLE01"
VARIABLE01 = is variable 01
$ cat variables.txt
TEST_VAR=TestValue
$ source variables.txt
$ echo "TEST_VAR = $TEST_VAR"
TEST_VAR = TestValue
目前的結構上,其實還有一些缺點,例如,把共同的部分透過 .common_tags
使用 extends
引入,而內容中已經有 needs
相依條件,這會造成如果其他的 job 還有相依關係時會無法建立,或造成 YAML 無法使用,因此這邊使用 echo 寫入 Tags 這方法其實不是最好的,只是為了方便展示,這部分也可以考慮透過特殊關鍵字透過取代複寫,如 sed
指令寫入 tags
對應的 Tags。
另外,目前的方法是基於有一個能夠讓 Runner 有唯一性的 Runner Tags,如果你是使用 GitLab.com 上的免費方案,沒有自己的 Runner 時,就無法辦到,因為 GitLab.com 上提供的免費 Runner,都是使用相同的 Tags,例如 gitlab-org
並無法決定是哪個 Runner。
但透過 Parent Child Pipeline 的方法來解決所有的 Job 要在同一個 Runner 上執行的這問題是適用的。看到這系列題目的你,有其他的解法嗎?歡迎一起討論。我是墨嗓(陳佑竹),期待這次的內容能帶給你實用的啟發與幫助。